package pl.maliboo.ftp.invokers
{
	import com.carlcalderon.arthropod.Debug;
	
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.filesystem.File;
	import flash.filesystem.FileMode;
	import flash.filesystem.FileStream;
	import flash.net.Socket;
	import flash.utils.ByteArray;
	import flash.utils.clearInterval;
	import flash.utils.getTimer;
	import flash.utils.setInterval;
	
	import pl.maliboo.ftp.Commands;
	import pl.maliboo.ftp.FTPCommand;
	import pl.maliboo.ftp.Responses;
	import pl.maliboo.ftp.core.FTPClient;
	import pl.maliboo.ftp.core.FTPInvoker;
	import pl.maliboo.ftp.errors.InvokeError;
	import pl.maliboo.ftp.events.FTPEvent;
	import pl.maliboo.ftp.utils.PassiveSocketInfo;
	
	import org.lexapp.ftp.RemoteFileBrowserApplication;

/*
	import com.carlcalderon.arthropod.Debug;
	
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.filesystem.File;
	import flash.filesystem.FileMode;
	import flash.filesystem.FileStream;
	import flash.net.Socket;
	import flash.utils.ByteArray;
	import flash.utils.clearInterval;
	import flash.utils.getTimer;
	import flash.utils.setInterval;
	
	import pl.maliboo.ftp.Commands;
	import pl.maliboo.ftp.FTPCommand;
	import pl.maliboo.ftp.FTPFile;
	import pl.maliboo.ftp.Responses;
	import pl.maliboo.ftp.core.FTPClient;
	import pl.maliboo.ftp.core.FTPInvoker;
	import pl.maliboo.ftp.errors.InvokeError;
	import pl.maliboo.ftp.events.FTPEvent;
	import pl.maliboo.ftp.utils.PassiveSocketInfo;
*/
	public class UploadInv extends FTPInvoker
	{
		/**
		 * maximum uplad time after which the upload is interrupted
		 * interuption is useful because it is followed by a listing of the folder to determine the real number of uploaded bytes. the upload can then be restarted (REST command) and the display of the upload progress can be updated
		 */
		public var maxUploadDuration:Number = 5000.0*(Math.random()+.5);
		/**
		 * after a file is entirely put into the buffer, the socket is closed even if the data hs not been entirely sent. the minUploadDuration forces the socket to stay open if the upload started a short time ago.
		 * useful for the end of big files or for very slow connections 
		 */
		public var minUploadDuration:Number = 3000.0*(Math.random()+.5);
		/**
		 * 
		 */
		private static const SEND_INTERVAL:Number = 30;

		private var positionForRestore:uint = 0;
		private var restoreMode:Boolean = false;
		public var localFile:String;
		public var remoteFile:String;
		
		public var sourceFile:FileStream;
		private var fileMode:String;
		private var passiveSocket:Socket;
		
		private var bytesTotal:int=0;
		private var bufferSize:uint = 	RemoteFileBrowserApplication.SOCKET_BUFFER_SIZE;
		
		private var interval:uint;
		
		private var startTime:int;
		
		/**
		 * 
		 * note lex: if localFile is null, it will upload an empty file
		 */
		public function UploadInv(client:FTPClient, localFile:String, remoteFile:String,positionForRestore:uint=0)
		{
			super(client);
			this.localFile = localFile;
			this.remoteFile = remoteFile;
			sourceFile = new FileStream();
			this.positionForRestore = positionForRestore;
		}
		
		override protected function startSequence ():void
		{
			if (localFile)
			{
				sourceFile.open(new File(localFile), FileMode.READ);
				if (positionForRestore>0)
				{
					Debug.log("UploadInv startSequence restoreMode "+positionForRestore,Debug.BLUE);
					restoreMode = true;
					sourceFile.position = positionForRestore;
				}
				else
				{
					Debug.log("UploadInv startSequence NOT IN restoreMode",Debug.GREEN);
					restoreMode = false;
				}
				trace("File has: "+sourceFile.bytesAvailable+" bytes");
			}
			else
			{
				restoreMode=false;
			}
			sendCommand(new FTPCommand(Commands.BINARY));
		}
		private var lastPasvMessage:String;
		override protected function responseHandler(evt:FTPEvent):void
		{
			Debug.log("UploadInv responseHandler "+evt.response.code,Debug.GREEN);
			switch (evt.response.code)
			{
				case Responses.COMMAND_OK:
					Debug.log("UploadInv responseHandler COMMAND_OK");
					sendCommand(new FTPCommand(Commands.PASV));
					break;
				case Responses.ENTERING_PASV:
					Debug.log("UploadInv responseHandler ENTERING_PASV");
					lastPasvMessage = evt.response.message;
					if (restoreMode == true)
					{
						Debug.log("UploadInv responseHandler restoreMode "+sourceFile);
						sendCommand(new FTPCommand(Commands.REST,positionForRestore.toString()));
						break;
					}
				case Responses.REST_OK:
					Debug.log("UploadInv responseHandler REST_OK or end of ENTERING_PASV");  
					passiveSocket = PassiveSocketInfo.createPassiveSocket(lastPasvMessage, handleConnect, handleData, handleIOErr, handleClose);
					break;
					
				case Responses.DATA_CONN_CLOSE:
					Debug.log("UploadInv responseHandler DATA_CONN_CLOSE");
					
					if (localFile) 
						sourceFile.close();
						
					trace(getTimer()+" ACK/"+(getTimer()-startTime))
					//passiveSocket.close();
					var downloadEvent:FTPEvent = new FTPEvent(FTPEvent.UPLOAD);
					downloadEvent.bytesTotal = bytesTotal;
					downloadEvent.file = remoteFile;
					downloadEvent.time = getTimer() - startTime;
					release(downloadEvent);
					break;
				case Responses.FILE_STATUS_OK:
					Debug.log("UploadInv responseHandler FILE_STATUS_OK "+restoreMode);
					if (localFile) 
						startSendingData();
					else
					{
						// create an empty file
						if (passiveSocket != null)
								passiveSocket.close();
					}
					break
				case Responses.NOT_FOUND:
					Debug.error("UploadInv responseHandler NOT_FOUND");
					// throw new Error("File not found!");
//					cleanUp ();
//					releaseWithError(new InvokeError(evt.response.message));
//					break;
				default:
					Debug.error("UploadInv responseHandler default => ERROR? --------------------------------------------------------------------------------------------");
/*					if (localFile) 
						sourceFile.close(); //TODO: wspolny cleanup?!
					if (interval>=0)
						clearInterval(interval);
					if (passiveSocket != null && passiveSocket.connected)
						passiveSocket.close();
*/					//releaseWithError(new InvokeError(evt.response.message));
			}
		}
		
		override protected function cleanUp ():void
		{
			if (localFile) 
				sourceFile.close(); //TODO: wspolny cleanup?!
			if (interval>=0)
				clearInterval(interval);
			if (passiveSocket != null && passiveSocket.connected)
				passiveSocket.close();
		}
		
		private function handleConnect (evt:Event):void
		{
			Debug.log("UploadInv handleConnect "+remoteFile);
			sendCommand(new FTPCommand(Commands.STOR, remoteFile));
		}
		
		private function handleIOErr(evt:IOErrorEvent):void
		{
			Debug.log("UploadInv handleIOErr "+evt)
		}
		
		private function handleClose(evt:Event):void
		{
			if (localFile) 
				Debug.log("UploadInv handleClose - Real socket close at "+sourceFile.position)
		}
		
		private function handleData (evt:ProgressEvent):void
		{
			trace("Upload SocketData")
		}
		
		private function handleProgress (evt:ProgressEvent):void
		{
			Debug.log("UploadInv handleProgress "+evt);
		}
		
		private function startSendingData():void
		{
			Debug.log("UploadInv startSendingData Start sending data! ("+passiveSocket.connected+")");
			startTime = getTimer();
			interval = setInterval(sendData, SEND_INTERVAL);
			//sendBlockData();
		}
		/*
		private function sendBlockData ():void
		{
			var ba:ByteArray = new ByteArray();
			sourceFile.readBytes(ba, bytes, Math.min(bufferSize, sourceFile.bytesAvailable - bytes));
			bytes += bufferSize;
			bufferSize = Math.min(bufferSize, sourceFile.bytesAvailable - bytes);
			
			passiveSocket.writeBytes(ba, 0, ba.bytesAvailable);
			passiveSocket.flush();
			
		}
		*/
		private function sendData():void
		{
			var timer:int = getTimer();
			var duration:uint = timer - startTime;
			
			//Debug.log("UploadInv sendData - sent: "+sourceFile.bytesAvailable+" - duration min: "+minUploadDuration+" - duration: "+duration+" - duration max: "+maxUploadDuration);
			//Debug.log("UploadInv sendData - sent: "+sourceFile.bytesAvailable+" / "+duration);
			
			try
			{
				if (sourceFile.bytesAvailable <= 0 || duration > maxUploadDuration)
				{
					//Debug.log("UploadInv sendData SocketClose MAX DURATION or ALL DATA SENT");
					if(duration >= minUploadDuration)
					{
						clearInterval(interval);
						passiveSocket.close();
						Debug.log("UploadInv sendData SocketClose :"+passiveSocket.connected);
						//release(new FTPEvent(FTPEvent.UPLOAD)); // TF1
					}
					return;
				}
				
				if (sourceFile.bytesAvailable < bufferSize)
					bufferSize = sourceFile.bytesAvailable;
				
				var ba:ByteArray = new ByteArray();
				sourceFile.readBytes(ba, 0, bufferSize);
				//trace(timer+" Bytes to read: "+ba.bytesAvailable+"/"+sourceFile.bytesAvailable)
				passiveSocket.writeBytes(ba, 0, ba.bytesAvailable);
				passiveSocket.flush();
			}
			catch(e:Error)
			{
				if (interval>=0)
				{
					clearInterval(interval);
					passiveSocket.close();
				}
				Debug.error("--------------------------------------------------------------------------------------------------------------------------------UploadInv sendData Error: "+e);
				//throw(e);
			}
			// TF1
			// Dispatching upload progress
/*			
			var progressEvent:FTPEvent = new FTPEvent(FTPEvent.UPLOAD_PROGRESS);
				progressEvent.progress		= Math.round((100 * sourceFile.position) / (sourceFile.bytesAvailable + sourceFile.position)); // Calculating %
				progressEvent.bytes			= sourceFile.position
				progressEvent.bytesTotal	= sourceFile.bytesAvailable;
				progressEvent.startTime		= startTime;
				progressEvent.now			= timer;
				client.dispatchEvent(progressEvent);*/
		}
		
		// TF1
		public function abort():void {
			clearInterval(interval);
			passiveSocket.close();
			trace(getTimer()+" SocketClose :"+passiveSocket.connected);
			release(new FTPEvent(FTPEvent.ABORT));
			return;
		}
		
	}
}